package com.indigon.parkStory.model.objects
{
	import com.indigon.parkStory.model.GameEvent;
	import com.indigon.parkStory.model.ModelMain;
	
	import flash.geom.Point;

	public class FrogModel extends ObjectModel
	{
		private static const JUMP_PUSH_MULTIPLIER 		: Number = 0.0075;
		private static const JUMP_LAND_PUSH_MULTIPLIER 	: Number = 0.0075;
		private static const JUMP_COUNT_DIVISOR   		: Number = 0.075;
		private static const LILLY_BALANCE_BORDER		: Number = -3.5;
		private static const MIN_JUMP_RANGE				: Number = 10;
		private static const BASIC_JUMP_RANGE			: Number = 150;
		private static const JUMP_RANGE_INCREASE		: Number = 15;
		private static const MIN_JUMP_RANGE_SQR			: Number = MIN_JUMP_RANGE * MIN_JUMP_RANGE;
		private static const JUMP_BUFFER_TIME			: Number = 0.6;
		public  static const ANG_VEL_PUSH_FORCE			: Number = 4;
		
		public var lilly    	  : LillyModel;
		public var willJump       : Boolean;
		public var isJumping      : Boolean;
		public var jumpAng        : Number;
		public var jumpRange	  : Number;
		private var _jumpDist     : Number;
		private var _diffPos  	  : Point;
		private var _jumpDest	  : Point;
		private var _jumpOrg  	  : Point;
		private var _jumpMaxCount : uint;
		private var _jumpCount    : uint;
		
		private var _jumpBuffer   : Point;
		
		
		public function FrogModel(model:ModelMain)
		{
			jumpRange = BASIC_JUMP_RANGE;
			super(model);
		}
		
		public override function uninit():void
		{
			lilly 	  = null;
			_diffPos  = null;
			_jumpDest = null;
			_jumpOrg  = null;
			super.uninit();
		}
		
		public function setInitialLilly(lilly:LillyModel):void
		{
			this.lilly = lilly;
			_diffPos = new Point(0, 0);
			set(lilly.pos.x, lilly.pos.y);
		}
		
		public function canJumpTo(x:Number, y:Number):Boolean
		{
			var dx : Number = pos.x - x;
			var dy : Number = pos.y - y;
			var inMaxRange : Boolean = ((dx * dx) + (dy * dy) <= (jumpRange * jumpRange));
			var inMinRange : Boolean = ((dx * dx) + (dy * dy) >= MIN_JUMP_RANGE_SQR);
			return (inMaxRange && inMinRange)
		}
		
		public function setJumpTo(x:Number, y:Number):void
		{
			if (!isJumping)
			{
				if (!isDead && canJumpTo(x, y))
				{
					willJump = true;
					_jumpDest = new Point(x, y);
				}
			}
			else
			{
				if (jumpArrival >= JUMP_BUFFER_TIME)
				{
					_jumpBuffer = new Point(x , y);
				}
			}
		}
		
		public function jump():void
		{
			willJump  = false;
			isJumping = true;
			
			jumpAng       = Math.atan2(_jumpDest.y - pos.y, _jumpDest.x - pos.x);
			_jumpDist     = Point.distance(_jumpDest, pos);
			_jumpOrg      = pos.clone();
			_jumpMaxCount = uint(_jumpDist * JUMP_COUNT_DIVISOR);
			_jumpCount    = 0;
			
			var pushForce : Point = Point.polar(_jumpDist * JUMP_PUSH_MULTIPLIER, jumpAng - Math.PI);
			
			lilly.jumpOff(pushForce);
			
			_model.addEvent(GameEvent.JUMP_START, lilly);
			
			lilly = null;
		}
		
		public function land():void
		{
			isJumping = false;
			
			var aliveFlag       : Boolean;
			var pushForceLength : Number = _jumpDist * JUMP_LAND_PUSH_MULTIPLIER;
			var pushForce       : Point  = Point.polar(pushForceLength, jumpAng);
			
			for each (var lillyCheck : LillyModel in _model.lillies)
			{
				// landed well.
				if (isInsideLilly(lillyCheck))
				{
					aliveFlag = true;
					lilly = lillyCheck;
					_diffPos = pos.subtract(lilly.pos);
					lillyCheck.jumpOn(pushForceLength, pushForce);
					_model.addEvent(GameEvent.JUMP_SUCCESS, lilly);
					
					break;
				}
			}
			
			if (!aliveFlag)
			{
				isDead = true;
				_model.addWave(pos.x, pos.y, pushForceLength, null);
				_model.addEvent(GameEvent.JUMP_FAIL);
			}
			else if (_jumpBuffer != null)
			{
				setJumpTo(_jumpBuffer.x, _jumpBuffer.y);
				_jumpBuffer = null;
			}
		}
		
		public function increaseRange():void
		{
			jumpRange += JUMP_RANGE_INCREASE;
		}
		
		public function resetRange():void
		{
			jumpRange = BASIC_JUMP_RANGE;
		}
		
		public override function update():void
		{
			if (isJumping)
			{
				pos = Point.interpolate(_jumpDest, _jumpOrg, jumpingEase(_jumpCount / _jumpMaxCount));
				_jumpCount++;
				
				if (_jumpCount > _jumpMaxCount)
				{
					land();
				}
			}
			else if (lilly != null)
			{
				pos.x = lilly.pos.x + _diffPos.x;
				pos.y = lilly.pos.y + _diffPos.y;
			}
		}
		
		private function isInsideLilly(lilly:LillyModel):Boolean
		{
			var newRad : Number = lilly.data.radius - LILLY_BALANCE_BORDER;
			var dx     : Number = pos.x - lilly.pos.x;		
			var dy     : Number = pos.y - lilly.pos.y;
			
			return (dx * dx) + (dy * dy) <= (newRad * newRad);
		}
		
		private function jumpingEase(t:Number):Number
		{
			return t * t;
		}
		
		public function get jumpArrival():Number
		{
			return _jumpCount / _jumpMaxCount;
		}
	}
}